import _ from 'lodash';
import { message, Modal } from 'antd';
import utils from "../../utils";
import page from "../../apis/page";
import { WeaLocaleProvider } from 'ecCom';
import { comsType } from "../../constants/form";
import { action, observable, toJS } from "mobx";
import { getTheme } from "../../components/designer/form/theme";
import { 
  generateSubComponents, 
  getDefaultData, 
  getViewableComponent,
  smoothWidths, 
  recalculateWidths
} from "../../components/designer/form/component";
import { 
  isWrapper, 
  isDataTable, 
  isNormal, 
  isFooter, 
  isFormHeader,
  isFormFooter, 
  isDisableForDataTable, 
  isTableLayout,
  isAddress
} from "../../components/designer/form/component/typeof";
import EE, { actions, getEventName, callbacks } from '../../components/designer/form/event/component';
import compatible from '../../components/designer/form/component/compatible';

const { getLabel } = WeaLocaleProvider;
const { FORM_HEADER, FORM_FOOTER, FOOTER, ADDRESS, DATATABLE } = comsType;
const defaultTheme = getTheme();

export default class ComponentStore {
  tableName = null; //物理表名
  
  @observable formHeader = {};
  @observable formFooter = {};
  @observable footer = {};
  @observable components = null;
  @observable selectedId = '';
  @observable theme = {};
  @observable codeBlock = null;

  @action init = appid => {
    if (!appid) return;

    let fetchData = null;

    this.initEE();

    if (_.isObject(appid)) {
      fetchData = Promise.resolve(appid);
    } else {
      fetchData = page.getDesign(appid);
    }

    return fetchData.then(all => {
      const coms = all.main;
      const subComs = all.sub;

      this.theme = all.style ? JSON.parse(all.style) : defaultTheme;
      this.codeBlock = all.codeBlock;
      this.tableName = all.tableName;

      if (!coms || !coms.length) {
        this.formHeader = this.getNewComponent({ type: FORM_HEADER });
        this.formFooter = this.getNewComponent({ type: FORM_FOOTER });
        this.footer = this.getNewComponent({ type: FOOTER });

        this.components = [];
      } else {
        // config为null时 组件的defaultData无法起效
        coms.forEach(com => {
          com.config = com.config || {};

          if (com.type === ADDRESS) {
            this.setFieldNameToAddress(com, subComs);
          }

          if (com.type === DATATABLE) {
            this.setDetailTableName(com, subComs);
          }
        });

        this.formHeader = coms.find(com => com.type === FORM_HEADER);
        this.formFooter = coms.find(com => com.type === FORM_FOOTER);
        this.footer = coms.find(com => com.type === FOOTER);

        if(!this.footer){
          this.footer = this.getNewComponent({ type: FOOTER });
        }
        
        this.components = this.convertToViewableComs(coms, all.sub); 
        
        const { com } = this.getComponentWith(true, c => c.config.selected);

        if (com) {
          this.selectedId = com.uuid;
        }
      }
      return coms || [];
    });
  }

  initEE() {
    EE.removeAllListeners();
    EE.on(actions.Delete, this.onRemove);
    EE.on(actions.Select, this.onSelectedIdChange);
    EE.on(actions.Add, this.addComponent);
  }

  convertToViewableComs = (coms, subComsMap) => {
    return coms.filter(com => getViewableComponent(com))
      .map(com => {
        const { coms } = com.config;

        if (coms) {
          com.config.coms = coms.map(subComId => subComsMap[subComId]);
        }
        
        com.config = compatible(com.type, com.config);

        return com;
      });
  }

  // isNew 是否为新拖动的组件(并未渲染过View), drop out之后应该移除
  @action onSortable = (changes) => {
    const coms = toJS(this.components);
    const newComs = utils.sort(coms, changes);

    this.components = newComs;
  }

  // 避免使用memoize后 com被缓存无法及时更新的问题
  @action onSelectedIdChange = comUUID => {
    this.selectedId = comUUID;
  }

  addNewComponent(newCom) {
    let { com, index } = this.getComponentWith(this.selectedId);

    if (!com || isFormFooter(com) || isFooter(com)) {
      return this.addComponent(newCom, this.components.length);
    }

    if (isFormHeader(com)) {
      return this.addComponent(newCom, 0);
    }

    const isNotWrapperCom = !isWrapper(newCom);
    const isSelectedComInWrapper = !!com.pid;

    // 当前选中的组件为明细表时，点击左侧组件自动为明细表添加一列
    if (isNotWrapperCom && isDataTable(com)) {
      const { uuid, config: { coms } } = com;
      let i = coms.findIndex(com => !com);
      
      newCom = { ...newCom, pid: uuid };
      i = ~i ? i : coms.length;

      return this.addComponentForWrapper(newCom, i);
    }

    if (isSelectedComInWrapper) {
      if (isNotWrapperCom) {
        newCom = { ...newCom, pid: com.pid };

        return this.addComponentForWrapper(newCom, index + 1);
      }
        index = this.findComponentIndex({ uuid: com.pid });
    }

    this.addComponent(newCom, index + 1);
  }

  @action onPropsChange = (key, value, comId) => {
    if (_.isObject(key)) {
      comId = value;
      value = void 0;
    }

    const { com } = this.getComponentWith(comId || this.selectedId);

    this.setValue(com, key, value);

    if (this.canSelect(com)) {
      this.selectedId = com.uuid;
    }

    if (!isNormal(com)) {
      const isFormFooter = this.selectedId === this.formFooter.uuid;
      const isFormHeader = this.selectedId === this.formHeader.uuid;
      const type = isFormFooter ? 'formFooter' : isFormHeader ? 'formHeader' : 'footer';

      this[type] = toJS(com);
    } else {
      this.components = toJS(this.components);
    }
  }

  setValue(com, key, value) {
    if (_.isString(key)) {
      return this._setValue(com, key, value);
    }

    if (_.isObject(key)) {
      const keys = _.keys(key);

      keys.forEach(k => {
        const v = key[k];

        this._setValue(com, k, v);
      });
    }
  }

  _setValue(com, key, value) {
    const { config: obj, type } = com;
    const keys = key.split('.');
    const len = keys.length;
    const _keys = [];
    let dftConfig = null;
    
    if (len === 1) return (obj[key] = value);

    return keys.reduce((prev, k, i) => {
      const isLast = len === i + 1;

      _keys.push(k);
      // 兼容新增配置属性（属性值为对象）的情况
      if (prev[k] === void 0) {
        dftConfig = dftConfig || this.getDefaultData(type);
        const dftValue = _keys.reduce((prev, k) => prev[k], dftConfig);

        if (dftValue !== void 0) {
          prev[k] = dftValue;
          return prev[k];
        }
      }

      if (!isLast) return prev[k];
      
      prev[k] = value;
    }, obj);
  }

  getDefaultData(type) {
    return _.cloneDeep(getDefaultData(type));
  }

  @action onRemove = (com) => {
    if (!com.existData) return this._onRemove(com);

    Modal.confirm({
      title: getLabel(504074, '删除插件'),
      content: (
        <span>
          {getLabel(518171, '此字段已经有用户提交了数据。')}
          <br />
          {getLabel(504080, '你确定要删除吗？')}
        </span>
      ),
      onOk: () => {
        this._onRemove(com);
      }
    });
  }

  _onRemove(com) {
    const index = this.findComponentIndex(com);

    if (~index) {
      const evtName = getEventName(callbacks.RemoveChild, com.uuid);

      this.removeComponent(index);
      EE.removeListener(evtName);
    } else {
      const [coms, i] = this.getSubComInWrapper(com);
      const { config } = this.components.find(({ uuid }) => uuid === com.pid);
      const widths = toJS(config.widths);
      const evtName = getEventName(callbacks.RemoveChild, com.pid);

      if (config.size) {
        coms[i] = '';
      } else {
        coms.splice(i, 1);
      }

      if (widths) {
        widths.splice(i, 1);
        config.widths = smoothWidths.forReduce(widths);
      }
      // 触发父组件内部绑定的删除回调方法，如（表格布局）
      EE.emit(evtName, com);
    }

    this.components = toJS(this.components);

    if (com.uuid === this.selectedId) {
      this.selectedId = '';
    }
  }

  getComponentWith(propVal, handle = c => c.uuid) {
    if (!this.components || _.isUndefined(propVal)) {
      return { com: null, index: -1 };
    }

    const coms = [...this.components, this.formHeader, this.formFooter, this.footer];
    let index = coms.findIndex(c => handle(c) === propVal);
    let com = coms[index];

    if (com) return { com, index };

    coms.filter(isWrapper).every(c => {
      const { coms: subComs } = c.config;

      index = subComs.findIndex(subCom => 
        subCom && handle(subCom) === propVal);
      com = subComs[index];

      return !com;
    });

    return { com, index };
  }

  getSubComInWrapper(com) {
    const wrappers = this.components.filter(c => isWrapper(c));
    let comInfo = null;

    wrappers.every(w => {
      const { coms } = w.config;
      const index = coms.findIndex(c => c && c.uuid === com.uuid);

      if (!~index) return true;

      comInfo = [coms, index];
    });

    return comInfo;
  }

  getComponents = () => {
    const coms = toJS(this.components);
    const allComs = [];

    if (!coms) return allComs;

    const _map = com => {
      const { title, complex } = com.config;

      com.name = title || '';
      com.config.selected = com.uuid === this.selectedId;

      // 复合组件 - 需要拆分子组件，便于后台生成多字段用于支持图表分析
      // 如地址
      if (complex) {
        const subComs = generateSubComponents(com);

        return [com, ...subComs]
      }
      return [com];
    };
    
    coms.forEach(com => {
      const { coms = [], widths } = com.config;

      allComs.push(..._map(com));

      if (coms.length) {
        const isDT = isDataTable(com);
        const newComs = coms.filter((com, index) => {
          if (!isDT || com) return true;

          widths && widths.splice(index, 1);
        });

        if(isDT){
          //重新计算宽度
        com.config.widths = recalculateWidths(widths);
        }

        newComs.filter(Boolean).forEach(subCom => {
          allComs.push(..._map(subCom));
        });

        com.config.coms = newComs.map(subCom => {
          if (!subCom) return '';
          
          // 保存需要将coms转成uuid
          return subCom.uuid;
        });
      }
    });

    allComs.unshift(toJS(this.formHeader));
    allComs.push(toJS(this.formFooter));
    allComs.push(toJS(this.footer));
    
    return allComs;
  }

  removeComponent(index) {
    this.components.splice(index, 1);
  }

  findComponentIndex(item) {
    if (!item) return;

    return this.components.findIndex(com => com.uuid === item.uuid);
  }

  addComponent = (item, index) => {
    const newCom = this.getNewComponent(item, index);

    if (_.isUndefined(index)) {
      return this.addNewComponent(newCom);
    }

    if (newCom.pid) {
      return this.addComponentForWrapper(newCom, index);
    }

    if (this.canSelect(newCom)) {
      this.selectedId = newCom.uuid;
    }

    this.components = utils.addItem(this.components, newCom, index);
  }

  // 拖拽组件到布局组件中
  dragComponentToWrapper(dragInfo, comIndex) {
    const { uuid: wrapperComUUID, index: newIndex } = dragInfo;
    const { com: wrapperCom } = this.getComponentWith(wrapperComUUID);
    const newCom = this.components[comIndex];

    if (!wrapperCom) return;
    if (isWrapper(newCom)) return message.warn(getLabel('512012','暂不支持嵌套布局'));
    
    newCom.pid = wrapperCom.uuid;
    this.components.splice(comIndex, 1);

    if (isTableLayout(wrapperCom)) {
      return this.addComponentForTable(wrapperCom, newCom, newIndex);
    }
      newCom.config.hidden = void 0;
      wrapperCom.config.coms[newIndex] = newCom;


    this.selectedId = newCom.uuid;
    this.components = toJS(this.components);
  }

  addNewComponentFromWrapper(dragInfo, newIndex) {
    const { uuid, index: subComIndex } = dragInfo;
    const { com } = this.getComponentWith(uuid);

    if (!com) return;

    const { coms, grid } = com.config;
    let subCom = coms[subComIndex];

    if (isTableLayout(com)) {
      const [rowIndex, colIndex] = subComIndex.split('_');
      const { comId } = grid[rowIndex][colIndex];
      const comIndex = coms.findIndex(com => com.uuid === comId);

      subCom = coms[comIndex];
      // 清空子组件的依赖关系
      delete grid[rowIndex][colIndex].comId;
      coms.splice(comIndex, 1);
    } else {
      coms[subComIndex] = '';
    }

    subCom.pid = '';
    this.addComponent(subCom, newIndex);
  }

  getNewComponent(item) {
    const { 
      type, name, pid,
      uuid = utils.UUID(),
      config = this.getDefaultData(type),
    } = item;
    const newCom = { type, name, pid, uuid, config };

    if (config.complex) {
      // 为复合组件的子组件初始化uuid
      config.complex.forEach(key => {
        const sub = config[key];

        sub.uuid = utils.UUID();
      });
    }

    return newCom;
  }

  addComponentForWrapper(item, index) {
    const newCom = this.getNewComponent(item);

    if (isWrapper(newCom)) return message.warn(getLabel('512012','暂不支持嵌套布局'));

    const wrapperComIndex = this.components.findIndex(com => item.pid === com.uuid);
    const wrapperCom = this.components[wrapperComIndex];

    if (!wrapperCom) return;
    if (isDataTable(wrapperCom) && isDisableForDataTable(newCom)) {
      return message.warn(getLabel('512013','子表暂不支持该组件'));
    }

    if (isTableLayout(wrapperCom)) {
      return this.addComponentForTable(wrapperCom, newCom, index);
    }

    const { config } = wrapperCom;
    const { coms, widths } = config;
    let replace = false;
    // 超出列布局的size
    const outOfBound = index + 1 > config.size || coms.filter(Boolean).length >= config.size

    if (!outOfBound) {
      // 明细表 子组件没有隐藏字段属性
      newCom.config.hidden = void 0;
    } else {
      newCom.pid = '';
      return this.addComponent(newCom, wrapperComIndex + 1);
    }

    // 自动补全空白的可拖拽位置
    if (!coms[index]) {
      coms[index] = newCom;
      replace = true;
    } else {
      coms.splice(index, 0, newCom);
    }

    if (widths && coms.length !== widths.length) {
      config.widths = smoothWidths.forAdd(toJS(widths), index, replace);
    }

    this.selectedId = newCom.uuid;
    this.components = toJS(this.components);
  }

  addComponentForTable(table, newCom, index) {
    const [rowIndex, colIndex] = _.isString(index) ? index.split('_') : index;
  
    table.config.coms.push(newCom);
    table.config.grid[rowIndex][colIndex].comId = newCom.uuid;
  
    this.selectedId = newCom.uuid;
    this.components = toJS(this.components);
  }

  canSelect(com) {
    return !isWrapper(com) || isDataTable(com);
  }

  @action copyField = copyFieldId => {
    // 过滤查找所需复制字段
    const coms = toJS(this.components);
    const index = coms.findIndex( c => c.uuid === copyFieldId);
    const newCom = _.cloneDeep(coms[index]);
    
    newCom.uuid = utils.UUID();
    delete newCom.existData;
    delete newCom.fieldId;
    
    if (isAddress(newCom)) {
      newCom.config.complex.forEach(key => {
        const sub = newCom.config[key];

        sub.uuid = utils.UUID();
      });
    }

    this.components.splice(index + 1, 0, newCom);
  }
  //为组件添加fieldname
  @action addFieldNameToCom = (data) => {
    const coms = toJS(this.components);

    this.setFieldNameToCom(data, coms);
    this.components = coms;
  }

  @action setFieldNameToCom = (data, coms) => {
    const { fields, tableName } = data;
    
    if (tableName) {
      this.tableName = tableName;
    }

    coms.forEach(com => {
      
      if (!com) return;
      //布局组件
      if (com.config.coms) {
        return this.setFieldNameToCom(data, com.config.coms);
      }
      //地址组件
      if (com.type === ADDRESS) {
        this.setFieldNameToAddress(com, fields);
      }
      
      if (fields[com.uuid]) {
        com.fieldName = fields[com.uuid].fieldName;
      }
    });
  }
  
  //对地址组件特殊结构 做相应处理
  @action setFieldNameToAddress = (com, data) => {
    let { province, city, district, detail } = com.config;

    [province, city, district, detail].forEach(subCom => {
      if (data[subCom.uuid] && data[subCom.uuid].fieldName) {
        subCom.fieldName = data[subCom.uuid].fieldName;
      };
    });
  }

  @action setDetailTableName = (com, data) => {
    const { coms } = com.config;

    if (!_.isEmpty(coms) && !_.isEmpty(data)) {
      if (data[coms[0]] && data[coms[0]].detailTable) {
        com.detailTable = data[coms[0]].detailTable; 
      }
    }   
  }

  //为明细子表添加表名
  @action addDetailTableName = (data) => {
    const { fields } = data;
    const coms = toJS(this.components);

    coms.forEach(com => {
      if (com.type === DATATABLE) {
        const { coms } = com.config;

        if (coms[0]) {
          const subComId = coms[0].uuid;

          com.detailTable = fields[subComId].detailTable;
        }   
      }
    });

    this.components = coms;
  }
}